﻿using System;
using System.Drawing;

using Storm.TextEditor.Editor;
using Storm.TextEditor.Editor.Text;

namespace Storm.TextEditor.Parser.Objects.Collections
{
	/// <summary>
	/// Represents a collection of FormatRange.
	/// </summary>
	public class FormatRangeCollection
		: BaseCollection
	{
		#region Fields

		private SyntaxDocument document = null;

		#endregion

		#region Properties

		/// <summary>
		/// Gets the document of the FormatRangeCollection.
		/// </summary>
		internal SyntaxDocument Document
		{
			get { return document; }
		}

		public FormatRange this[int index]
		{
			get { return List[index] as FormatRange; }
		}

		#endregion

		#region Methods

		#region Public

		/// <summary>
		/// Adds the given FormatRange to the FormatRangeCollection.
		/// </summary>
		/// <param name="item">Item to add.</param>
		/// <returns>Index of the added item.</returns>
		public int Add(FormatRange item)
		{
			item.Document = this.Document;
			item.Apply();

			return this.List.Add(item);
		}

		/// <summary>
		/// Removes the given FormatRange from the FormatRangeCollection.
		/// </summary>
		/// <param name="item">Item to remove.</param>
		public void Remove(FormatRange item)
		{
			this.List.Remove(item);
		}

		/// <summary>
		/// Returns whether the given Row contains the FormatRanges of the FormatRangeCollection.
		/// </summary>
		/// <param name="row">Row to check whether contains the FormatRanges of the FormatRangeCollection.</param>
		/// <returns>Whether the given Row contains the FormatRanges of the FormatRangeCollection.</returns>
		public bool RowContainsFormats(Row row)
		{
			foreach (FormatRange formatRange in this)
			{
				if (formatRange.Bounds.FirstRow <= row.Index && formatRange.Bounds.LastRow >= row.Index)
					return true;
			}

			return false;
		}

		/// <summary>
		/// Merges the given coordinates into the FormatRanges of the FormatRangeCollection.
		/// </summary>
		/// <param name="x">X coordinate to merge.</param>
		/// <param name="y">Y coordinate to merge.</param>
		/// <returns>The new FormatRange.</returns>
		public FormatRange MergeFormatRanges(int x, int y)
		{
			FormatRange formatRange = null;
			foreach (FormatRange exFormatRange in this)
			{
				if (exFormatRange.Contains(x, y) != 0)
					continue;

				if (exFormatRange.BackColor != Color.Empty)
				{
					if (formatRange == null)
						formatRange = new FormatRange();

					formatRange.BackColor = exFormatRange.BackColor;
				}

				if (exFormatRange.ForeColor != Color.Empty)
				{
					if (formatRange == null)
						formatRange = new FormatRange();

					formatRange.ForeColor = exFormatRange.ForeColor;
				}

				if (exFormatRange.WaveColor != Color.Empty)
				{
					if (formatRange == null)
						formatRange = new FormatRange();

					formatRange.WaveColor = exFormatRange.WaveColor;
				}
			}

			return formatRange;
		}

		/// <summary>
		/// Expands the FormatRange at the given coordinates by the given text.
		/// </summary>
		/// <param name="x">X coordinate of the FormatRange.</param>
		/// <param name="y">Y coordinate of the FormatRange.</param>
		/// <param name="text">Text to expand with.</param>
		public void Expand(int x, int y, string text)
		{
			if (this.RowContainsFormats(this.Document[y]) == true)
			{
				string newText = text.Replace(Environment.NewLine, "\n");
				int length = newText.Length;

				string[] lines = newText.Split('\n');
				foreach (FormatRange formatRange in this)
				{
					int res = formatRange.Contains(x, y);
					if (res == 0)
					{
						formatRange.Bounds.LastRow += lines.Length - 1;
						if (formatRange.Bounds.LastRow == y + lines.Length - 1)
							formatRange.Bounds.LastColumn += lines[lines.Length - 1].Length;
					}
					else if (res == -1)
					{
						formatRange.Bounds.FirstRow += lines.Length - 1;
						formatRange.Bounds.LastRow += lines.Length - 1;
						if (formatRange.Bounds.FirstRow == y + lines.Length - 1)
						{
							if (lines.Length > 1)
								formatRange.Bounds.FirstColumn -= x;

							formatRange.Bounds.FirstColumn += lines[lines.Length - 1].Length;
						}
					}
				}
			}
		}

		/// <summary>
		/// Shrinks the FormatRange at the given TextRange's position by the TextRange's bounds.
		/// </summary>
		/// <param name="textRange">TextRange to find FormatRange at.</param>
		public void Shrink(TextRange textRange)
		{
			string text = Document.GetRange(textRange);
			string newText = text.Replace(Environment.NewLine, "\n");
			int length = newText.Length;
			string[] lines = newText.Split('\n');

			foreach (FormatRange formatRange in this)
			{
				int res = formatRange.Contains(textRange.FirstColumn, textRange.FirstRow);
				int res2 = formatRange.Contains(textRange.LastColumn, textRange.LastRow);

				int rows = textRange.LastRow - textRange.FirstRow;
				if (res == -1 && res2 == -1)
				{
					formatRange.Bounds.FirstRow -= rows;
					formatRange.Bounds.LastRow -= rows;
					if (formatRange.Bounds.FirstRow == textRange.FirstRow + lines.Length - 1)
					{
						if (lines.Length > 1)
							formatRange.Bounds.FirstColumn -= textRange.FirstColumn;

						formatRange.Bounds.FirstColumn -= lines[lines.Length - 1].Length;
					}
					else if (formatRange.Bounds.FirstRow == textRange.FirstRow)
					{
						if (lines.Length > 1)
							formatRange.Bounds.FirstColumn += textRange.FirstColumn;

						formatRange.Bounds.FirstColumn -= lines[lines.Length - 1].Length;
					}
				}
				else if (res == -1 && res2 == 0)
				{
					formatRange.Bounds.FirstRow -= rows;
					formatRange.Bounds.LastRow -= rows;
					formatRange.Bounds.FirstColumn = textRange.FirstColumn;
				}
				else if (res == 0 && res2 == 0)
				{
					formatRange.Bounds.LastRow -= rows;
					if (formatRange.Bounds.LastRow == textRange.FirstRow)
					{
						if (rows == 0)
							formatRange.Bounds.LastColumn -= lines[lines.Length - 1].Length;
						else
							formatRange.Bounds.LastColumn = textRange.FirstColumn + formatRange.Bounds.LastColumn;
					}
				}
				else if (res == 0 && res2 == 1)
				{
					formatRange.Bounds.LastColumn = textRange.FirstColumn - 1;
					formatRange.Bounds.LastRow = textRange.FirstRow;
				}

				if (res == -1 && res2 == 1)
					formatRange.Bounds.SetBounds(-1, -1, -1, -1);
			}
		}

		#endregion

		#endregion

		/// <summary>
		/// Initializes a new instance of FormatRangeCollection.
		/// </summary>
		public FormatRangeCollection(SyntaxDocument document)
		{
			this.document = document;
		}
	}
}
